# -*- coding: utf-8 -*-
"""
Created on Sat Jan 05 22:22:11 2013

@author: Winand
"""

from __future__ import with_statement
OP_DEF_V, OP_SET_I, OP_GET_I = 1, 2, 3
OP_DEL_I, OP_IN, OP_GET_ALL = 4, 5, 6
OP_LEN, OP_LOCK, OP_ULOCK = 7, 8, 9
OP_TEST = 10
OP_ADDITEM, OP_EXISTS = 11, 12
ANSWER_OK, ANSWER_EXISTS, ANSWER_NOT_EXISTS = 1, 2, 3
from settings import SHARED_SERVER_ADDR, SHARED_SERVER_PORT
from webapi.server.socketpipe import SocketPipe
from webapi.server import shareddata as d
HDLR = "dictionary" #server request handler name

class RemoteDict(SocketPipe):
    def __send_data(self, var):
        if not self.connected:
            self.connect(self.server, self.port)
        res = self.send(var)
        if not self.connected:
            self.close()
        return res
        
    def __init__(self, name, serv=None, port=None, d={}, perm=False):
        self.name=name
        self.server = serv or SHARED_SERVER_ADDR
        self.port = port or SHARED_SERVER_PORT
        self.connected=False #init blocking connection
        self.__send_data((HDLR, OP_DEF_V,self.name,d,perm))

    def add(self, i, y):
        if type(i) is not tuple: i=(i,)
        if self.__send_data((HDLR, OP_ADDITEM,self.name,i,y)) == ANSWER_NOT_EXISTS:
            raise LookupError()
    
    def __setitem__(self, i, y):
        if type(i) is not tuple: i=(i,)
        if self.__send_data((HDLR, OP_SET_I,self.name,i,y)) == ANSWER_NOT_EXISTS:
            raise LookupError()
        
    def __getitem__(self, i):
        if type(i) is not tuple: i=(i,)
        rcv = self.__send_data((HDLR, OP_GET_I,self.name,i))
        if rcv[0] == ANSWER_EXISTS: return rcv[1]
        else: raise LookupError()
        
    def get(self, i, d=None):
        try: return self.__getitem__(i)
        except LookupError: return d
        
    def pop(self, i, d=None):
        small_lock = True if not self.connected else False
        if small_lock: self.lock()
        try: 
            ret = self.__getitem__(i)
            self.__delitem__(i)
        except LookupError: ret = d
        if small_lock: self.ulock()
        return ret  
        
    def getAll(self, i, d=None):
        if type(i) is not tuple: i=(i,)
        rcv = self.__send_data((HDLR, OP_GET_ALL,self.name,i,d))
        if rcv[0] == ANSWER_EXISTS: return rcv[1]
        else: raise LookupError()
        
    def __delitem__(self, i):
        if type(i) is not tuple: i=(i,)
        if self.__send_data((HDLR, OP_DEL_I,self.name,i)) == ANSWER_NOT_EXISTS:
            raise LookupError()
        
    def __repr__(self):
        return repr(self.__getitem__(()))
        
    def __len__(self):
        rcv = self.__send_data((HDLR, OP_LEN,self.name,()))
        if rcv[0] == ANSWER_EXISTS: return rcv[1]
        else: raise LookupError()
        
    def __contains__(self, i):
        if self.__send_data((HDLR, OP_IN,self.name,i)) == ANSWER_EXISTS:
            return True
        
    def lock(self):
        if not self.connected:
            self.connect(self.server, self.port)
            self.connected=True
            self.__send_data((HDLR, OP_LOCK,self.name))
            
    def ulock(self):
        if self.connected:
            self.__send_data((HDLR, OP_ULOCK,self.name))
            self.connected=False
            self.close()
            
    def __enter__(self): self.lock()
    def __exit__(self, type, value, traceback): self.ulock()
    
    @staticmethod
    def exists(name, serv=None, port=None):
        pipe = SocketPipe()
        pipe.connect(serv or SHARED_SERVER_ADDR, port or SHARED_SERVER_PORT)
        pipe.send_data((HDLR, OP_EXISTS, name))
        ret = pipe.recv_data()
        pipe.close()
        return True if ret == ANSWER_EXISTS else False
        
def __dictionary(data_pipe, op, name, *args):
        if op == OP_EXISTS:                         #check var
            data_pipe.send_data(d.exists(name))
        if op == OP_DEF_V:                          #new var
            #lock is acquired in addVar!
            data_pipe.send_data( \
                d.addVar(name, args[0], args[1]))
        elif op == OP_LOCK:                         #lock connection
            data_pipe.send_data(ANSWER_OK)
            return True
        elif op == OP_ULOCK:                        #unlock connection
            data_pipe.send_data(ANSWER_OK)
            return False

        try:
            if op == OP_SET_I or op == OP_DEL_I \
                            or op == OP_ADDITEM:    #set, del
                item = d.data[name]
                for i in args[0][:-1]: item = item[i]
                if op == OP_SET_I: #set item
                    item[args[0][-1]] = args[1]
                elif op == OP_ADDITEM:
                    item[args[0][-1]].append(args[1])
                else: #del item
                    del item[args[0][-1]]
                data_pipe.send_data(ANSWER_OK)
                d.changed(name) #save changes if needed
            elif op == OP_IN:                       #item in
                item = d.data[name]
                if args[0] in item:
                    data_pipe.send_data(ANSWER_EXISTS)
                else: data_pipe.send_data(ANSWER_NOT_EXISTS)
        except Exception:
            data_pipe.send_data(ANSWER_NOT_EXISTS)
            
        try:
            if op == OP_GET_ALL:                    #get all
                item = d.data[name]
                for i in args[0][:-1]: item = item[i]
                ret = dict([(i, item[i].get(args[0][-1], args[1])) for i in item])
                data_pipe.send_data((ANSWER_EXISTS, ret))
            elif op == OP_GET_I or op == OP_LEN:    #get, len
                item = d.data[name]
                for i in args[0]: item = item[i]
                data_pipe.send_data((ANSWER_EXISTS, \
                    item if op == OP_GET_I else len(item)))
        except Exception:
            data_pipe.send_data((ANSWER_NOT_EXISTS,))
            
def dictionary(data_pipe, *args):
    blocked = False #session lock, default - no
    d.lock(args[1]) #acquire lock, if var exists
    while True:
        ret = __dictionary(data_pipe, *args)
        if ret != None: blocked = ret #if block changed
        if blocked: args = data_pipe.recv_data()[1:]
        else: break
    d.ulock(args[1]) #release lock

#kill monitor thread
def flush_and_stop_monitor():
    d.flush_timer.cancel()
    d.check_changes()
